package cards;

import java.util.List;

/**
 * Class representing a single playing card from a standard 52-card deck.
 * @author Eric Andresen
 */
public class Card implements Comparable<Card> {
  public enum Face {
    ACE('A'),
    TWO('2'),
    THREE('3'),
    FOUR('4'),
    FIVE('5'),
    SIX('6'),
    SEVEN('7'),
    EIGHT('8'),
    NINE('9'),
    TEN('T'),
    JACK('J'),
    QUEEN('Q'),
    KING('K');

    private char display;

    private Face(char display) {
      this.display = display;
    }

    public char getChar() {
      return this.display;
    }

    public static Face fromChar(char display) {
      for (Face face : Face.values()) {
        if (display == face.getChar()) {
          return face;
        }
      }
      throw new IllegalArgumentException("Invalid face: " + display);
    }
  }

  public enum Suit {
    CLUBS('c'),
    DIAMONDS('d'),
    HEARTS('h'),
    SPADES('s');

    private char display;

    private Suit(char display) {
      this.display = display;
    }

    public char getChar() {
      return this.display;
    }

    public static Suit fromChar(char display) {
      for (Suit suit : Suit.values()) {
        if (display == suit.getChar()) {
          return suit;
        }
      }
      throw new IllegalArgumentException("Invalid suit: " + display);
    }
  }

   /**
    * Create a new Card from a two-character string.
    * @param type Two character representation of the card.
    * This expects and insists on formatting the strings such as 'As'
    * representing the Ace of Spades. A Ten of Hearts would be 'Th'.
    * Case is irrelevant.
    */ 
   public static Card getCardForString(String type) {
      if (type.length() != 2) {
         throw new IllegalArgumentException("Invalid type length");
      }

      char faceChar = type.charAt(0); 
      char suitChar = type.charAt(1); 

      Suit suit = Suit.fromChar(suitChar);
      Face face = Face.fromChar(faceChar);
      return new Card(suit, face);
   }

   /** Card's suit value. */
   private Suit suit;
   /** Card's face value. */
   private Face face;

   /**
    * Create a new Card with the specified Suit and Value.
    * @param suit Suit of card.
    * @param value Face value.
    */
   public Card(Suit suit, Face face) {
      this.suit = suit;
      this.face = face;
   }

   /** Retrieve card's face value. */
   public Face getFace() { return face; }
   /** Retrieve card's suit. */
   public Suit getSuit() { return suit; }

   public int hashCode() {
     return getFace().ordinal() * 31 + getSuit().ordinal();
   }

   /**
    * Check for equality to another Card.
    * @param obj Object to compare to.
    * @return true if the card's suit and face value match.
    */
   public boolean equals(Object obj) {
      boolean ret = false;

      if (obj instanceof Card) {
         Card c = (Card)obj;
         ret = ((c.getSuit() == this.getSuit()) && (c.getFace() == this.getFace()));
      }

      return ret;
   }

   /**
    * Compare one Card with another.
    * @return int representing equality.<br>
    *   0 for equal, -1 for less than, 1 for greater than.
    */
   public int compareTo(Card c) {
      int val = getFace().compareTo(c.getFace());
      if (val == 0) {
        val = getSuit().compareTo(c.getSuit());
      }
      return val;
   }

   /**
    * Convert the Card into a string representation of its Suit and Value.
    * Ace of Spades becomes 'As'; Ten of Hearts becomes 'Th'.
    * @return String representation of the card.
    */ 
   public String toString() {
     return "" + getFace().getChar() + getSuit().getChar();
   }

   public static String toString(List<Card> cards) {
     StringBuilder sb = new StringBuilder();

     for (Card card : cards) {
       sb.append(card.toString()).append(',');
     }
     if (!cards.isEmpty()) {
       sb.deleteCharAt(sb.length()-1);
     }

     return sb.toString();
   }

   /**
    * Print out the full Deck.
    */
   public static void main(String[] args) {
     for (Suit suit : Suit.values()) {
       for (Face face : Face.values()) {
            System.out.print((new Card(suit, face)).toString()+" ");
       }
       System.out.println();
     }
   }
}
